package jcu.salt.client;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import jcu.sal.common.StreamID;
import jcu.sal.common.CommandFactory.Command;
import jcu.sal.common.cml.CMLDescriptions;
import jcu.sal.common.exceptions.SALDocumentException;
import jcu.sal.common.sml.SMLDescription;
import jcu.sal.common.sml.SMLDescriptions;
import jcu.salt.Logger;

/**
 * this is the main glue to the SAL by use of RMI connections provided
 * by the abstract {@link RmiClient}.
 * 
 * @author SAL-T team
 *
 */
public class SaltRmiClient extends RmiClient implements SaltClient {

	/**
	 * holds all sensors available
	 */
	private Set<SMLDescription> sensorList;
	
	/**
	 * holds all threads responsible for polling the sensors
	 */
	private List<SensorThread> threads;

	public SaltRmiClient(String rmiName, String agentRMIRegIP, String localIP)
			throws RemoteException {
		super(rmiName, agentRMIRegIP, localIP);
	}

	@Override
	public void run() {

		Logger.log("start the salt rmi client");

		updateSensorList();

		threads = new ArrayList<SensorThread>();

		threads.add(new SensorThread(SensorThread.PROTOCOL_OSDATA, this));
		threads.add(new SensorThread(SensorThread.PROTOCOL_1WIRE, this));
		threads.add(new SensorThread(SensorThread.PROTOCOL_SNMP, this));
		threads.add(new SensorThread(SensorThread.PROTOCOL_VIDEO, this));

		for (SensorThread thread : threads) {
			Logger.log("try to start thread for protocol "
					+ thread.getProtocolName());	
			thread.start();
		}		
		Logger.log("started all threads");		
	}
	
	@Override
	public void stop() throws RemoteException {
		stopThreads();
		super.stop();
		super.release();
	}
	
	/**
	 * stops the polling of the sensor values
	 * and waits for the associated threads to shutdown
	 */
	private void stopThreads() {
		for (SensorThread thread : threads) {
			Logger.log("shutdown thread for "
					+ thread.getProtocolName());
			thread.stop();
		}
	}

	/**
	 * provides access to the attribute sensor List
	 * 
	 * @see #updateSensorList()
	 * 
	 * @return reference to the sensorList object
	 */
	private synchronized Set<SMLDescription> getSensorList() {
		return sensorList;
	}

	/**
	 * provides filtered access to the attribute sensor list the result is
	 * calculated for every call!!!
	 * 
	 * @see #updateSensorList()
	 * 
	 * @param protocolName
	 *            filtering by the protocol name
	 * @return a set containing all sensors matching the given protocol name
	 */
	public Set<SMLDescription> getSensorList(String protocolName) {
		Set<SMLDescription> result = new HashSet<SMLDescription>();
		Set<SMLDescription> sensors = getSensorList();
		if (sensors != null) {
			for (SMLDescription tmp : sensors) {
				if (tmp.getProtocolName().startsWith(protocolName)) {
					result.add(tmp);
				}
			}
		}
		return result;
	}

	/**
	 * updates the attribute sensorList using the descriptions provided by the
	 * agent when failing, the sensorList is set to null and false is returned
	 * 
	 * @return true for success
	 */
	private  boolean updateSensorList() {
		boolean success = false;
		try {
			sensorList = new SMLDescriptions(agent.listActiveSensors())
					.getDescriptions();
			success = true;
		} catch (SALDocumentException e) {
			sensorList = null;
			e.printStackTrace();
		}
		return success;
	}

	@Override
	public StreamID startStream(Command c, String sid) throws SalClientException {
		StreamID result = null;
		try {
			result = agent.setupStream(c, sid);
			if(result!=null)
				agent.startStream(result);
		} catch (Exception e) {
			e.printStackTrace();
			throw new SalClientException(e.getMessage());
		}
		return result;
	}
	
	@Override
	public void stopStream(StreamID sid){
		try {
			agent.terminateStream(sid);
		} catch (Exception e) {
			System.err.println("Error stopping stream "+sid.toString());
			//e.printStackTrace();
		}
	}

	@Override
	public CMLDescriptions getCML(SMLDescription smldesc) throws SalClientException {
		CMLDescriptions result = null;
		try {
			result =  new CMLDescriptions(agent.getCML(String.valueOf(smldesc.getSID())));
		} catch (Exception e) {
			System.err.println(e.getLocalizedMessage());
			e.printStackTrace();
			throw new SalClientException(e.getLocalizedMessage());
		}
		return result;
	}

	@Override
	public String getSalID() {
		return agent.getID();
	}

}
